﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BigDataFramework.Helpers;
using System.Web;
using BigData.DataContracts.Objects.Reddit;
using BigData.DataContracts.Objects.BigData;
using BigDataFramework.Repositories;
using MongoDB.Bson;
using BigDataFramework.Library.Helpers;

namespace BigDataFramework.Library.Objects.DataAdapters.Reddit
{
    public static class RedditDataAdapterHelper
    {
        public static string DefaultPassword = "Skipper5!";
        public static string DefaultUserName = "GilliganWilloughby";
        public static string endpoint = "http://www.reddit.com/";
    }
    /// <summary>
    /// This is singleton, used by all adapters for calls to the api
    /// </summary>
    public static class RedditAdapter
    {
        public static string _modhash;
        public static List<RestSharp.RestResponseCookie> _cookies;
        public static RestSharp.RestClient _client;
        public static string _user;

        public static BigDataUserToRedditUserData RetrieveRedditUserIdForBigDataUserId(BigDataUser user)
        {
            MongoDBRepository dbContext = new MongoDBRepository();
            return dbContext.ReadOne<BigDataUserToRedditUserData>(ru => ru.BigDataUserId == ObjectId.Parse(user._id));
        }

        /// <summary>
        /// Hopefully this gets the cookie and modhash maybe?
        /// </summary>
        /// <param name="userName"></param>
        static RedditAdapter()
        {   
            //initialize the user
            _user = null;

            //initialize the cookie jar
            _cookies = new List<RestSharp.RestResponseCookie>();

            //Set up the client with the end point
            _client = new RestSharp.RestClient();
            _client.BaseUrl = RedditDataAdapterHelper.endpoint;
        }

        /// <summary>
        /// Removes all cookies used by this adapter.
        /// </summary>
        public static void ScrubClient()
        {
            _client.CookieContainer = null;
        }

        /// <summary>
        /// Pulls the modhash and cookie from a RedditResponse object
        /// </summary>
        /// <param name="response"></param>
        public static void GetModHashAndCookies(string user, string password)
        {
            //if the current modhash and cookies are already for the user being passed then skip this process
            if (_user == user)
                return;

            //_user marks which user the current set of cookies and modhash are for
            _user = user;

            _client.Authenticator = new RestSharp.SimpleAuthenticator("user", user, "passwd", password);
            RestSharp.RestRequest request = new RestSharp.RestRequest(RestSharp.Method.POST);
            request.Resource = string.Format("api/login/{0}/", user);
            request.AddParameter("api_type", "json");

            RestSharp.IRestResponse response = _client.Execute(request);

            //check for errors
            if (response.ErrorException != null)
                throw new Exception(response.ErrorMessage);

            //remove the authenticator so the username and password are not sent in clear text for the remaining requests
            _client.Authenticator = null;

            //Pull out the cookies and store them
            foreach (RestSharp.RestResponseCookie cookie in response.Cookies)
                _cookies.Add(cookie);

            //Add the cookies to the cookie jar
            _client.CookieContainer = new System.Net.CookieContainer();
            foreach (RestSharp.RestResponseCookie cookie in _cookies)
            {
                _client.CookieContainer.Add(new System.Net.Cookie(cookie.Name, cookie.Value, cookie.Path, cookie.Domain));
            }
        }

        /// <summary>
        /// Removes entries from the end of the list until the list only contains elements newer then the specified start date
        /// </summary>
        public static bool TrimOldResults<T>(ref List<T> results, DateTime startDate)
            where T : UserCreatedThing
        {
            for (int i = 0; i < results.Count(); i++)
            {
                //convert long date to date time
                DateTime createDate = DateTimeHelper.FromUnixTimeStamp(results[i].created_utc);

                //if the date is before the start date then remove it and everything after it and break
                if (createDate < startDate)
                {
                    results = results.GetRange(0, i);
                    return true;
                }

            }
            return false;
        }

        /// <summary>
        /// Reddit paginates return results and only returns 25 at a time. This function will get the next 25. The request should already point to a resource
        /// </summary>
        /// <param name="previous"></param>
        /// <returns></returns>
        public static string GetNextPage(RestSharp.RestRequest request, string previous)
        {
            //TODO: Test this
            //remove any "after" parameters that may already exist
            request.Parameters.RemoveAll(parameter => parameter.Name == "after");

            //add the new after parameter
            request.AddParameter("after", previous);
            
            RestSharp.IRestResponse response = _client.Execute(request);

            return response.Content;
        }

        /// <summary>
        /// Gets all the whatever between the start date and now
        /// </summary>
        /// <param name="startDate"></param>
        /// <param name="request"></param>
        /// <returns></returns>
        public static List<T> GetAll<T>(DateTime startDate, DateTime endDate, RestSharp.RestRequest request)
            where T : UserCreatedThing
        {
            // List to be returned
            List<T> finalList = new List<T>();

            // Fire off the request
            RestSharp.IRestResponse response = _client.Execute(request);

            // Deserialize the response
            RedditResponse<Thing<T>> rr = JSONHelper.DeserializeFromJson<RedditResponse<Thing<T>>>(response.Content);

            // Break it down and remove those that don't fit into the date convert it to a list of comments
            List<T> list = rr.data.children.Select(c => c.data).ToList<T>();

            // Comments are paginated into groups of 25. If there are fewer than 25 comments then there is only one page
            //no need to continue so weed out those after the end date and return
            if (list.Count() < 25)
            {
                TrimOldResults(ref list, startDate);
                return TrimNewResults<T>(endDate, list);
            }

            // While all the results on the current page are valid
            while (!TrimOldResults<T>(ref list, startDate))
            {
                // Add the good comments to the final list
                foreach (T element in list)
                    finalList.Add(element);
                // Get the next page
                string nextPage = GetNextPage(request, list[list.Count() - 1].name);
                // Test if the next page has anything on it.
                RedditResponse<Thing<T>> nextComments = JSONHelper.DeserializeFromJson<RedditResponse<Thing<T>>>(nextPage);
                // If the next page is empty then break and return
                if (nextComments.data.children.Count == 0)
                    break;
                // Else break down the returned data into a list of comments and repeat
                list = rr.data.children.Select(c => c.data).ToList<T>();
            }
            return TrimNewResults<T>(endDate, finalList);
        }

        /// <summary>
        /// Removes any entries that were created after the end date
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="endDate"></param>
        /// <param name="list"></param>
        /// <returns></returns>
        public static List<T> TrimNewResults<T>(DateTime endDate, List<T> list)
            where T : UserCreatedThing
        {
            List<T> finalList = new List<T>();
            foreach (T element in list)
            {
                //if the create date is less than the end date add it to the final list
                if (DateTimeHelper.FromUnixTimeStamp(element.created_utc) < endDate)
                    finalList.Add(element);
            }
            return finalList;
        }
        
    }
}